{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from exp.nb_05 import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Jump_to notebook introduction in lesson 10 video](https://course19.fast.ai/videos/?lesson=10&t=3167)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Early stopping"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Better callback cancellation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Jump_to lesson 10 video](https://course19.fast.ai/videos/?lesson=10&t=3230)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train,y_train,x_valid,y_valid = get_data()\n",
    "train_ds,valid_ds = Dataset(x_train, y_train),Dataset(x_valid, y_valid)\n",
    "nh,bs = 50,512\n",
    "c = y_train.max().item()+1\n",
    "loss_func = F.cross_entropy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = DataBunch(*get_dls(train_ds, valid_ds, bs), c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class Callback():\n",
    "    _order=0\n",
    "    def set_runner(self, run): self.run=run\n",
    "    def __getattr__(self, k): return getattr(self.run, k)\n",
    "    \n",
    "    @property\n",
    "    def name(self):\n",
    "        name = re.sub(r'Callback$', '', self.__class__.__name__)\n",
    "        return camel2snake(name or 'callback')\n",
    "    \n",
    "    def __call__(self, cb_name):\n",
    "        f = getattr(self, cb_name, None)\n",
    "        if f and f(): return True\n",
    "        return False\n",
    "\n",
    "class TrainEvalCallback(Callback):\n",
    "    def begin_fit(self):\n",
    "        self.run.n_epochs=0.\n",
    "        self.run.n_iter=0\n",
    "    \n",
    "    def after_batch(self):\n",
    "        if not self.in_train: return\n",
    "        self.run.n_epochs += 1./self.iters\n",
    "        self.run.n_iter   += 1\n",
    "        \n",
    "    def begin_epoch(self):\n",
    "        self.run.n_epochs=self.epoch\n",
    "        self.model.train()\n",
    "        self.run.in_train=True\n",
    "\n",
    "    def begin_validate(self):\n",
    "        self.model.eval()\n",
    "        self.run.in_train=False\n",
    "\n",
    "class CancelTrainException(Exception): pass\n",
    "class CancelEpochException(Exception): pass\n",
    "class CancelBatchException(Exception): pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class Runner():\n",
    "    def __init__(self, cbs=None, cb_funcs=None):\n",
    "        self.in_train = False\n",
    "        cbs = listify(cbs)\n",
    "        for cbf in listify(cb_funcs):\n",
    "            cb = cbf()\n",
    "            setattr(self, cb.name, cb)\n",
    "            cbs.append(cb)\n",
    "        self.stop,self.cbs = False,[TrainEvalCallback()]+cbs\n",
    "\n",
    "    @property\n",
    "    def opt(self):       return self.learn.opt\n",
    "    @property\n",
    "    def model(self):     return self.learn.model\n",
    "    @property\n",
    "    def loss_func(self): return self.learn.loss_func\n",
    "    @property\n",
    "    def data(self):      return self.learn.data\n",
    "\n",
    "    def one_batch(self, xb, yb):\n",
    "        try:\n",
    "            self.xb,self.yb = xb,yb\n",
    "            self('begin_batch')\n",
    "            self.pred = self.model(self.xb)\n",
    "            self('after_pred')\n",
    "            self.loss = self.loss_func(self.pred, self.yb)\n",
    "            self('after_loss')\n",
    "            if not self.in_train: return\n",
    "            self.loss.backward()\n",
    "            self('after_backward')\n",
    "            self.opt.step()\n",
    "            self('after_step')\n",
    "            self.opt.zero_grad()\n",
    "        except CancelBatchException: self('after_cancel_batch')\n",
    "        finally: self('after_batch')\n",
    "\n",
    "    def all_batches(self, dl):\n",
    "        self.iters = len(dl)\n",
    "        try:\n",
    "            for xb,yb in dl: self.one_batch(xb, yb)\n",
    "        except CancelEpochException: self('after_cancel_epoch')\n",
    "\n",
    "    def fit(self, epochs, learn):\n",
    "        self.epochs,self.learn,self.loss = epochs,learn,tensor(0.)\n",
    "\n",
    "        try:\n",
    "            for cb in self.cbs: cb.set_runner(self)\n",
    "            self('begin_fit')\n",
    "            for epoch in range(epochs):\n",
    "                self.epoch = epoch\n",
    "                if not self('begin_epoch'): self.all_batches(self.data.train_dl)\n",
    "\n",
    "                with torch.no_grad(): \n",
    "                    if not self('begin_validate'): self.all_batches(self.data.valid_dl)\n",
    "                self('after_epoch')\n",
    "            \n",
    "        except CancelTrainException: self('after_cancel_train')\n",
    "        finally:\n",
    "            self('after_fit')\n",
    "            self.learn = None\n",
    "\n",
    "    def __call__(self, cb_name):\n",
    "        res = False\n",
    "        for cb in sorted(self.cbs, key=lambda x: x._order): res = cb(cb_name) or res\n",
    "        return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "learn = create_learner(get_model, loss_func, data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TestCallback(Callback):\n",
    "    _order=1\n",
    "    def after_step(self):\n",
    "        print(self.n_iter)\n",
    "        if self.n_iter>=10: raise CancelTrainException()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "run = Runner(cb_funcs=TestCallback)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "run.fit(3, learn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Other callbacks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class AvgStatsCallback(Callback):\n",
    "    def __init__(self, metrics):\n",
    "        self.train_stats,self.valid_stats = AvgStats(metrics,True),AvgStats(metrics,False)\n",
    "        \n",
    "    def begin_epoch(self):\n",
    "        self.train_stats.reset()\n",
    "        self.valid_stats.reset()\n",
    "        \n",
    "    def after_loss(self):\n",
    "        stats = self.train_stats if self.in_train else self.valid_stats\n",
    "        with torch.no_grad(): stats.accumulate(self.run)\n",
    "    \n",
    "    def after_epoch(self):\n",
    "        print(self.train_stats)\n",
    "        print(self.valid_stats)\n",
    "        \n",
    "class Recorder(Callback):\n",
    "    def begin_fit(self):\n",
    "        self.lrs = [[] for _ in self.opt.param_groups]\n",
    "        self.losses = []\n",
    "\n",
    "    def after_batch(self):\n",
    "        if not self.in_train: return\n",
    "        for pg,lr in zip(self.opt.param_groups,self.lrs): lr.append(pg['lr'])\n",
    "        self.losses.append(self.loss.detach().cpu())        \n",
    "\n",
    "    def plot_lr  (self, pgid=-1): plt.plot(self.lrs[pgid])\n",
    "    def plot_loss(self, skip_last=0): plt.plot(self.losses[:len(self.losses)-skip_last])\n",
    "        \n",
    "    def plot(self, skip_last=0, pgid=-1):\n",
    "        losses = [o.item() for o in self.losses]\n",
    "        lrs    = self.lrs[pgid]\n",
    "        n = len(losses)-skip_last\n",
    "        plt.xscale('log')\n",
    "        plt.plot(lrs[:n], losses[:n])\n",
    "\n",
    "class ParamScheduler(Callback):\n",
    "    _order=1\n",
    "    def __init__(self, pname, sched_funcs): self.pname,self.sched_funcs = pname,sched_funcs\n",
    "        \n",
    "    def begin_fit(self):\n",
    "        if not isinstance(self.sched_funcs, (list,tuple)):\n",
    "            self.sched_funcs = [self.sched_funcs] * len(self.opt.param_groups)\n",
    "\n",
    "    def set_param(self):\n",
    "        assert len(self.opt.param_groups)==len(self.sched_funcs)\n",
    "        for pg,f in zip(self.opt.param_groups,self.sched_funcs):\n",
    "            pg[self.pname] = f(self.n_epochs/self.epochs)\n",
    "            \n",
    "    def begin_batch(self): \n",
    "        if self.in_train: self.set_param()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LR Finder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NB: You may want to also add something that saves the model before running this, and loads it back after running - otherwise you'll lose your weights!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Jump_to lesson 10 video](https://course19.fast.ai/videos/?lesson=10&t=3545)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LR_Find(Callback):\n",
    "    _order=1\n",
    "    def __init__(self, max_iter=100, min_lr=1e-6, max_lr=10):\n",
    "        self.max_iter,self.min_lr,self.max_lr = max_iter,min_lr,max_lr\n",
    "        self.best_loss = 1e9\n",
    "        \n",
    "    def begin_batch(self): \n",
    "        if not self.in_train: return\n",
    "        pos = self.n_iter/self.max_iter\n",
    "        lr = self.min_lr * (self.max_lr/self.min_lr) ** pos\n",
    "        for pg in self.opt.param_groups: pg['lr'] = lr\n",
    "            \n",
    "    def after_step(self):\n",
    "        if self.n_iter>=self.max_iter or self.loss>self.best_loss*10:\n",
    "            raise CancelTrainException()\n",
    "        if self.loss < self.best_loss: self.best_loss = self.loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NB: In fastai we also use exponential smoothing on the loss. For that reason we check for `best_loss*3` instead of `best_loss*10`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "learn = create_learner(get_model, loss_func, data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "run = Runner(cb_funcs=[LR_Find, Recorder])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "run.fit(2, learn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "run.recorder.plot(skip_last=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "run.recorder.plot_lr()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Converted 05b_early_stopping.ipynb to exp/nb_05b.py\r\n"
     ]
    }
   ],
   "source": [
    "!python notebook2script.py 05b_early_stopping.ipynb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
